Skip to content

feat: Add three new operators for comparing arrays, commonly needed in workflow automation systems.#130

Merged
diegoholiveira merged 2 commits intodiegoholiveira:mainfrom
iyk2h:feature/array-operators
Dec 17, 2025
Merged

feat: Add three new operators for comparing arrays, commonly needed in workflow automation systems.#130
diegoholiveira merged 2 commits intodiegoholiveira:mainfrom
iyk2h:feature/array-operators

Conversation

@iyk2h
Copy link
Copy Markdown

@iyk2h iyk2h commented Nov 30, 2025

Summary

Add three new operators for comparing arrays, commonly needed in workflow automation systems.

New Operators

Operator Description Example
contains_all Returns true if all elements of the second array exist in the first {"contains_all": [["a","b","c"], ["a","b"]]} → true
contains_any Returns true if any element of the second array exists in the first {"contains_any": [["a","b"], ["x","a"]]} → true
contains_none Returns true if no elements of the second array exist in the first {"contains_none": [["a","b"], ["x","y"]]} → true

Use Case

I'm building a workflow automation system where users define conditions in the UI:

  • "If selected options contain all of ['VIP', 'Premium'] → send to Slack"
  • "If tags contain any of ['urgent', 'important'] → notify immediately"
  • "If categories contain none of ['blocked', 'spam'] → proceed"

The current in operator only checks if a single value exists in an array. These new operators enable array-to-array comparison.

Usage

// Check if user has all required roles
logic := strings.NewReader(`{"contains_all": [{"var": "roles"}, ["admin", "editor"]]}`)
data := strings.NewReader(`{"roles": ["admin", "editor", "viewer"]}`)

var result bytes.Buffer
jsonlogic.Apply(logic, data, &result)
// result: true

// Check if any priority tag exists
logic := strings.NewReader(`{"contains_any": [{"var": "tags"}, ["urgent", "important"]]}`)
data := strings.NewReader(`{"tags": ["normal", "urgent"]}`)
// result: true

// Check if content has no blocked words
logic := strings.NewReader(`{"contains_none": [{"var": "words"}, ["spam", "blocked"]]}`)
data := strings.NewReader(`{"words": ["hello", "world"]}`)
// result: true

Changes

  • arrays.go - New operators implementation
  • arrays_test.go - Comprehensive test cases (24 tests)

Tests

All tests passing:

=== RUN TestContainsAll
--- PASS: TestContainsAll (0.00s)
=== RUN TestContainsAny
--- PASS: TestContainsAny (0.00s)
=== RUN TestContainsNone
--- PASS: TestContainsNone (0.00s)
PASS

Notes

  • I can add README documentation if you'd like
  • Open to renaming operators if you prefer different naming conventions
  • Happy to split into separate PRs if preferred

#129

…tains_none)

Add three new operators for comparing arrays:

  - contains_all: Check if all elements of array B exist in array A
  - contains_any: Check if any element of array B exists in array A
  - contains_none: Check if no elements of array B exist in array A

  These operators are useful for workflow automation systems where users
  define conditions like:
  - "If selected options contain ALL of ['VIP', 'Premium']"
  - "If tags contain ANY of ['urgent', 'important']"
  - "If categories contain NONE of ['blocked', 'spam']"

  Includes comprehensive test cases for each operator.
@iyk2h iyk2h changed the title Add three new operators for comparing arrays, commonly needed in workflow automation systems. feet: Add three new operators for comparing arrays, commonly needed in workflow automation systems. Nov 30, 2025
@iyk2h iyk2h changed the title feet: Add three new operators for comparing arrays, commonly needed in workflow automation systems. feat: Add three new operators for comparing arrays, commonly needed in workflow automation systems. Nov 30, 2025
Copy link
Copy Markdown
Owner

@diegoholiveira diegoholiveira left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documentation: before the LICENSE in our README.md, let's a small documentation about those custom operations and a warning stating that those custom operators is not part of the official spec and may or may not be deprecated in the future.

@TotalTechGeek @beeme1mr this may be included in the final spec are you working on?

arrays.go Outdated
"github.com/diegoholiveira/jsonlogic/v3/internal/typing"
)

func init() {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's merge this init into https://github.com/diegoholiveira/jsonlogic/blob/main/operation.go#L98

Like:

/* CUSTOM OPERATORS */
operators["contains_all"] = containsAll
operators["contains_any"] = containsAny
operators["contains_none"] = containsNone

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review! I've made the requested changes:

  1. README.md: Added documentation for custom operators before the LICENSE section, including a warning that these operators are not part of the
    official spec and may be deprecated in the future.

  2. Code structure: Removed init() from arrays.go and moved the operator registration to operation.go with a /* CUSTOM OPERATORS */ comment.

All tests pass. Let me know if you'd like any further changes!

Copy link
Copy Markdown

@TotalTechGeek TotalTechGeek Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might elect to fold this into a Community Extension for set / array querying, and perhaps roll it into the recommended baseline for JSON Logic implementations.


I think right now, for the bottom two, you'd have to combine some every and in like,

{
  "some": [
    { "val": "tags" },
    { "in": [{ "val": [] }, ["urgent", "important"]] }
  ]
}

But it's definitely more cumbersome. 😄

I think contains_all would be difficult to recreate in most implementations, as it requires scope traversal to use with every and in, and we haven't quite agreed on that syntax yet in proposals.

all(['admin', 'editor'], in(@, @.^.^.roles))

a.k.a

{
 "all": [
  ["admin", "editor"],
  {
   "in": [
    {"val": []},
    {
     "val": [ [2], "roles" ]
    }
   ]
  }
 ]
}

That works in:

But nowhere else quite right now. And that's def so much more verbose than what's proposed here.

- Add Custom Operators section to README.md before LICENSE
- Move operator registration from arrays.go init() to operation.go
- Mark custom operators with /* CUSTOM OPERATORS */ comment
@diegoholiveira diegoholiveira merged commit 7ede255 into diegoholiveira:main Dec 17, 2025
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants